﻿using System;

namespace Kivensoft.XlsWriter
{
    public class Style : IComparable<Style>, IEquatable<Style> {
        public Font CurrentFont { get; set; }
        public Fill CurrentFill { get; set; }
        public Border CurrentBorder { get; set; }
        public NumberFormat CurrentNumberFormat { get; set; }
        public CellXf CurrentCellXf { get; set; }
        public int InternalID { get; set; }
		public string Name { get; private set; }
        

        public Style() {
            CurrentFont = new Font();
            CurrentFill = new Fill();
            CurrentBorder = new Border();
            CurrentNumberFormat = new NumberFormat();
            CurrentCellXf = new CellXf();
            Name = "Style_" + LowLevel.CreateUniqueName(16);
        }

        public Style(string name) : this() { Name = name; }

        public class CellXf : IComparable<CellXf>, IEquatable<CellXf> {
            public enum HorizontalAlignValue { left, center, right,
                fill, justify, general, centerContinuous, distributed, none,
            }

            public enum VerticallAlignValue { bottom, top, center,
                justify, distributed, none,
            }

            public enum TextBreakValue { wrapText, shrinkToFit, none, }

            public enum TextDirectionValue { horizontal, vertical, }

            public bool Locked { get; set; }
            public bool Hidden { get; set; }
            public HorizontalAlignValue HorizontalAlign { get; set; }
            public VerticallAlignValue VerticalAlign { get; set; }
            public TextBreakValue Alignment { get; set; }
            public int InternalID { get; set; }
  
            private int textRotation;
            private TextDirectionValue textDirection; 

            public int TextRotation {
                get { return textRotation; }
                set {
                    textRotation = value;
                    TextDirection = TextDirectionValue.horizontal;
                    CalculateInternalRotation();
                }
            }

            public TextDirectionValue TextDirection {
                get { return textDirection; }
                set {
                    textDirection = value;
                    CalculateInternalRotation();
                }
            }

            public bool ForceApplyAlignment { get; set; }
            
            public CellXf() {
                HorizontalAlign = HorizontalAlignValue.none;
                Alignment = TextBreakValue.none;
                textDirection = TextDirectionValue.horizontal;
                VerticalAlign = VerticallAlignValue.none;
                textRotation = 0;
            }

            public int CalculateInternalRotation() {
                if (textRotation < -90 || textRotation > 90)
                    throw new FormatException("The rotation value (" + textRotation.ToString() + "°) is out of range. Range is form -90° to +90°");
                if (textDirection == TextDirectionValue.vertical)
                    return 255;
                else return textRotation >= 0 ? textRotation : 90 - textRotation;
                
            }

            public bool Equals(CellXf other) {
				return other != null && HorizontalAlign == other.HorizontalAlign
					&& Alignment == other.Alignment
					&& TextDirection == other.TextDirection
					&& TextRotation == other.TextRotation
					&& VerticalAlign == other.VerticalAlign
					&& ForceApplyAlignment == other.ForceApplyAlignment
					&& Locked == other.Locked
					&& Hidden == other.Hidden;
            }

            public int CompareTo(CellXf other) {
                return InternalID.CompareTo(other.InternalID);
            }

            public CellXf Copy() {
				return new CellXf() {
					HorizontalAlign = HorizontalAlign,
					Alignment = Alignment,
					TextDirection = TextDirection,
					TextRotation = TextRotation,
					VerticalAlign = VerticalAlign,
					ForceApplyAlignment = ForceApplyAlignment,
					Locked = Locked,
					Hidden = Hidden
				};
            }
        }

        public class Font : IComparable<Font>, IEquatable<Font> {
            public const string DEFAULTFONT = "宋体";
            public enum VerticalAlignValue { subscript, superscript, none, }
            public enum SchemeValue { major, minor, none, }

            private int size;
            public int Size {
                get { return size; }
                set {
                    if (value < 8) { size = 8; }
                    else if (value > 75) { size = 72; }
                    else { size = value; }
                }
            }

            public string Name { get; set; }
            public string Family { get; set; }
            public int ColorTheme { get; set; }
            public string ColorValue { get; set; }
            public SchemeValue Scheme { get; set; }
            public VerticalAlignValue VerticalAlign { get; set; }
            public bool Bold { get; set; }
            public bool Italic { get; set; }
            public bool Underline { get; set; }
            public bool DoubleUnderline { get; set; }
            public bool Strike { get; set; }
            public string Charset { get; set; }
            public int InternalID { get; set; }
            public bool IsDefaultFont {
                get {
                    Font temp = new Font();
                    return Equals(temp); 
                }
            }

            public Font() {
                size = 9;
                Name = DEFAULTFONT;
                Family = "2";
                ColorTheme = 1;
                ColorValue = string.Empty;
                Charset = string.Empty;
                Scheme = SchemeValue.minor;
                VerticalAlign = VerticalAlignValue.none;
            }

            public bool Equals(Font other) {
                return other != null && Bold == other.Bold
					&& ColorTheme == other.ColorTheme
					&& DoubleUnderline == other.DoubleUnderline
					&& Family == other.Family
					&& Italic == other.Italic
					&& Name == other.Name
					&& Scheme == other.Scheme
					&& VerticalAlign == other.VerticalAlign
					&& Charset == other.Charset
					&& Size == other.Size
					&& Strike == other.Strike
					&& Underline == other.Underline;
            }

            public int CompareTo(Font other) {
                return InternalID.CompareTo(other.InternalID);
            }

            public Font Copy() {
				return new Font() {
					Bold = Bold,
					Charset = Charset,
					ColorTheme = ColorTheme,
					VerticalAlign = VerticalAlign,
					DoubleUnderline = DoubleUnderline,
					Family = Family,
					Italic = Italic,
					Name = Name,
					Scheme = Scheme,
					Size = Size,
					Strike = Strike,
					Underline = Underline
				};
            }
        }

        public class Fill : IComparable<Fill>, IEquatable<Fill> {
            public const string DEFAULTCOLOR = "FF000000";

            public enum PatternValue { none, solid, darkGray, mediumGray,
                lightGray, gray0625, gray125,
            }

            public enum FillType { patternColor, fillColor, }

            public static string GetPatternName(PatternValue pattern) {
				string output = string.Empty;
                switch (pattern) {
                    case PatternValue.none:
                        output = "none"; break;
                    case PatternValue.solid:
                        output = "solid"; break;
                    case PatternValue.darkGray:
                        output = "darkGray"; break;
                    case PatternValue.mediumGray:
                        output = "mediumGray"; break;
                    case PatternValue.lightGray:
                        output = "lightGray"; break;
                    case PatternValue.gray0625:
                        output = "gray0625"; break;
                    case PatternValue.gray125:
                        output = "gray125"; break;
                    default:
                        output = "none"; break;
                }
                return output;
            }

            public int IndexedColor { get; set; }
            public PatternValue PatternFill { get; set; }
            public string ForegroundColor { get; set; }
            public string BackgroundColor { get; set; }
            public int InternalID { get; set; }
            public Fill() {
                IndexedColor = 64;
                PatternFill = PatternValue.none;
                ForegroundColor = DEFAULTCOLOR;
                BackgroundColor = DEFAULTCOLOR;
            }
            public Fill(string forground, string background) {
                BackgroundColor = background;
                ForegroundColor = forground;
                IndexedColor = 64;
                PatternFill = PatternValue.solid;
            }

            public Fill(string value, FillType filltype) {
                if (filltype == FillType.fillColor) {
                    BackgroundColor = value;
                    ForegroundColor = DEFAULTCOLOR;
                }
                else {
                    BackgroundColor = DEFAULTCOLOR;
                    ForegroundColor = value;
                }
                IndexedColor = 64;
                PatternFill = PatternValue.solid;
            }

            public void SetColor(string value, FillType filltype) {
                if (filltype == FillType.fillColor) {
                    ForegroundColor = value;
                    BackgroundColor = DEFAULTCOLOR;
                }
                else {
                    ForegroundColor = DEFAULTCOLOR;
                    BackgroundColor = value;
                }
                PatternFill = PatternValue.solid;
            }

            public bool Equals(Fill other) {
				return other != null && IndexedColor == other.IndexedColor
					&& PatternFill == other.PatternFill
					&& ForegroundColor == other.ForegroundColor
					&& BackgroundColor == other.BackgroundColor;
            }

            public int CompareTo(Fill other) {
                return InternalID.CompareTo(other.InternalID);
            }

			public Fill Copy() {
				return new Fill() {
					BackgroundColor = BackgroundColor,
					ForegroundColor = ForegroundColor,
					IndexedColor = IndexedColor,
					PatternFill = PatternFill,
				};
			}
        }

        public class Border : IComparable<Border>, IEquatable<Border> {
            public enum StyleValue { none, hair, dotted, dashDotDot,
                dashDot, dashed, thin, mediumDashDotDot, slantDashDot,
                mediumDashDot, mediumDashed, medium, thick, s_double,
            }

            public static string GetStyleName(StyleValue style) {
                string output = string.Empty;
                switch (style) {
                    case StyleValue.none:
                    case StyleValue.hair:
                        break;
                    case StyleValue.dotted:
                        output = "dotted"; break;
                    case StyleValue.dashDotDot:
                        output = "dashDotDot"; break;
                    case StyleValue.dashDot:
                        output = "dashDot"; break;
                    case StyleValue.dashed:
                        output = "dashed"; break;
                    case StyleValue.thin:
                        output = "thin"; break;
                    case StyleValue.mediumDashDotDot:
                        output = "mediumDashDotDot"; break;
                    case StyleValue.slantDashDot:
                        output = "slantDashDot"; break;
                    case StyleValue.mediumDashDot:
                        output = "mediumDashDot"; break;
                    case StyleValue.mediumDashed:
                        output = "mediumDashed"; break;
                    case StyleValue.medium:
                        output = "medium"; break;
                    case StyleValue.thick:
                        output = "thick"; break;
                    case StyleValue.s_double:
                        output = "double"; break;
                }
                return output;
            }

            public StyleValue LeftStyle { get; set; }
            public StyleValue RightStyle { get; set; }
            public StyleValue TopStyle { get; set; }
            public StyleValue BottomStyle { get; set; }
            public StyleValue DiagonalStyle { get; set; }
            public bool DiagonalDown { get; set; }
            public bool DiagonalUp { get; set; }
            public string LeftColor { get; set; }
            public string RightColor { get; set; }
            public string TopColor { get; set; }
            public string BottomColor { get; set; }
            public string DiagonalColor { get; set; }
            public int InternalID { get; set; }
            public Border() {
                BottomColor = string.Empty;
                TopColor = string.Empty;
                LeftColor = string.Empty;
                RightColor = string.Empty;
                DiagonalColor = string.Empty;
                LeftStyle = StyleValue.none;
                RightStyle = StyleValue.none;
                TopStyle = StyleValue.none;
                BottomStyle = StyleValue.none;
                DiagonalStyle = StyleValue.none;
                DiagonalDown = false;
                DiagonalUp = false;
            }

            public bool Equals(Border other) {
				return other != null && BottomColor == other.BottomColor
					&& BottomStyle == other.BottomStyle
					&& DiagonalColor == other.DiagonalColor
					&& DiagonalDown == other.DiagonalDown
					&& DiagonalStyle == other.DiagonalStyle
					&& DiagonalUp == other.DiagonalUp
					&& LeftColor == other.LeftColor
					&& LeftStyle == other.LeftStyle
					&& RightColor == other.RightColor
					&& RightStyle == other.RightStyle
					&& TopColor == other.TopColor
					&& TopStyle == other.TopStyle;
            }

            public bool IsEmpty() {
				return BottomColor == string.Empty
					&& TopColor == string.Empty
					&& LeftColor == string.Empty
					&& RightColor == string.Empty
					&& DiagonalColor == string.Empty
					&& LeftStyle == StyleValue.none
					&& RightStyle == StyleValue.none
					&& TopStyle == StyleValue.none
					&& BottomStyle == StyleValue.none
					&& DiagonalStyle == StyleValue.none
					&& DiagonalDown == false
					&& DiagonalUp == false;
            }

            public int CompareTo(Border other) {
                return InternalID.CompareTo(other.InternalID);
            }

            public Border Copy() {
				return new Border() {
					BottomColor = BottomColor,
					BottomStyle = BottomStyle,
					DiagonalColor = DiagonalColor,
					DiagonalDown = DiagonalDown,
					DiagonalStyle = DiagonalStyle,
					DiagonalUp = DiagonalUp,
					LeftColor = LeftColor,
					LeftStyle = LeftStyle,
					RightColor = RightColor,
					RightStyle = RightStyle,
					TopColor = TopColor,
					TopStyle = TopStyle,
				};
            }
        }

        public class NumberFormat : IComparable<NumberFormat>, IEquatable<NumberFormat> {
            public const int CUSTOMFORMAT_START_NUMBER = 124;
            public enum FormatNumber {
                /// <summary>No format / Default</summary>
                none = 0,
                /// <summary>Format: 0</summary>
                format_1 = 1,
                /// <summary>Format: 0.00</summary>
                format_2 = 2,
                /// <summary>Format: #,##0</summary>
                format_3 = 3,
                /// <summary>Format: #,##0.00</summary>
                format_4 = 4,
                /// <summary>Format: $#,##0_);($#,##0)</summary>
                format_5 = 5,
                /// <summary>Format: $#,##0_);[Red]($#,##0)</summary>
                format_6 = 6,
                /// <summary>Format: $#,##0.00_);($#,##0.00)</summary>
                format_7 = 7,
                /// <summary>Format: $#,##0.00_);[Red]($#,##0.00)</summary>
                format_8 = 8,
                /// <summary>Format: 0%</summary>
                format_9 = 9,
                /// <summary>Format: 0.00%</summary>
                format_10 = 10,
                /// <summary>Format: 0.00E+00</summary>
                format_11 = 11,
                /// <summary>Format: # ?/?</summary>
                format_12 = 12,
                /// <summary>Format: # ??/??</summary>
                format_13 = 13,
                /// <summary>Format: m/d/yyyy</summary>
                format_14 = 14,
                /// <summary>Format: d-mmm-yy</summary>
                format_15 = 15,
                /// <summary>Format: d-mmm</summary>
                format_16 = 16,
                /// <summary>Format: mmm-yy</summary>
                format_17 = 17,
                /// <summary>Format: mm AM/PM</summary>
                format_18 = 18,
                /// <summary>Format: h:mm:ss AM/PM</summary>
                format_19 = 19,
                /// <summary>Format: h:mm</summary>
                format_20 = 20,
                /// <summary>Format: h:mm:ss</summary>
                format_21 = 21,
                /// <summary>Format: m/d/yyyy h:mm</summary>
                format_22 = 22,
                /// <summary>Format: #,##0_);(#,##0)</summary>
                format_37 = 37,
                /// <summary>Format: #,##0_);[Red](#,##0)</summary>
                format_38 = 38,
                /// <summary>Format: #,##0.00_);(#,##0.00)</summary>
                format_39 = 39,
                /// <summary>Format: #,##0.00_);[Red](#,##0.00)</summary>
                format_40 = 40,
                /// <summary>Format: mm:ss</summary>
                format_45 = 45,
                /// <summary>Format: [h]:mm:ss</summary>
                format_46 = 46,
                /// <summary>Format: mm:ss.0</summary>
                format_47 = 47,
                /// <summary>Format: ##0.0E+0</summary>
                format_48 = 48,
                /// <summary>Format: #</summary>
                format_49 = 49,
                /// <summary>Custom Format (ID 164 and higher)</summary>
                custom = 164,
            }
            public FormatNumber Number { get; set; }
            public int CustomFormatID { get; set; }
            public string CustomFormatCode { get; set; }
            public bool IsCustomFormat {
                get { return (Number ==  FormatNumber.custom); }
            }
            public int InternalID { get; set; }
            public NumberFormat() {
                Number = FormatNumber.none;
                CustomFormatCode = string.Empty;
                CustomFormatID = CUSTOMFORMAT_START_NUMBER;
            }

            public bool Equals(NumberFormat other) {
				return other != null && CustomFormatCode == other.CustomFormatCode
					&& CustomFormatID == other.CustomFormatID
					&& Number == other.Number;
            }

            public int CompareTo(NumberFormat other) {
                return InternalID.CompareTo(other.InternalID);
            }

            public NumberFormat Copy() {
				return new NumberFormat() {
					CustomFormatCode = CustomFormatCode,
					CustomFormatID = CustomFormatID,
					Number = Number
				};
            }

        }

        public static class BasicStyles {
            private enum StyleEnum {
                bold, italic, boldItalic, underline, doubleUnderlien,
                strike, dateFormat, roundFormat, borderFrame,
                borderFrameHeader, dottedFill_0_125, mergeCellStyle,
            }

            private static Style bold, italic, boldItalic, underline,
				doubleUnderline, strike, dateFormat, roundFormat, borderFrame,
				borderFrameHeader, dottedFill_0_125, mergeCellStyle;

            public static Style Bold
            { get { return GetStyle(StyleEnum.bold); } }
            public static Style Italic
            { get { return GetStyle(StyleEnum.italic); } }
            public static Style BoldItalic
            { get { return GetStyle(StyleEnum.boldItalic); } }
            public static Style Underline
            { get { return GetStyle(StyleEnum.underline); } }
            public static Style DoubleUnderline
            { get { return GetStyle(StyleEnum.doubleUnderlien); } }
            public static Style Strike
            { get { return GetStyle(StyleEnum.strike); } }
            public static Style DateFormat
            { get { return GetStyle(StyleEnum.dateFormat); } }
            public static Style RoundFormat
            { get { return GetStyle(StyleEnum.roundFormat); } }
            public static Style BorderFrame
            { get { return GetStyle(StyleEnum.borderFrame); } }
            public static Style BorderFrameHeader
            { get { return GetStyle(StyleEnum.borderFrameHeader); } }
            public static Style DottedFill_0_125
            { get { return GetStyle(StyleEnum.dottedFill_0_125); } }
            public static Style MergeCellStyle
            { get { return GetStyle(StyleEnum.mergeCellStyle); } }

            private static Style GetStyle(StyleEnum value) {
                Style s = null;
                switch (value) {
                    case StyleEnum.bold:
                        if (bold == null) {
							bold = new Style();
                            bold.CurrentFont.Bold = true;
                        }
                        s = bold;
                        break;
                    case StyleEnum.italic:
                        if (italic == null) {
                            italic = new Style();
                            italic.CurrentFont.Italic = true;
                        }
                        s = italic;
                        break;
                    case StyleEnum.boldItalic:
                        if (boldItalic == null) {
                            boldItalic = new Style();
                            boldItalic.CurrentFont.Italic = true;
                            boldItalic.CurrentFont.Bold = true;
                        }
                        s = boldItalic;
                        break;
                    case StyleEnum.underline:
                        if (underline == null) {
                            underline = new Style();
                            underline.CurrentFont.Underline = true;
                        }
                        s = underline;
                        break;
                    case StyleEnum.doubleUnderlien:
                        if (doubleUnderline == null) {
                            doubleUnderline = new Style();
                            doubleUnderline.CurrentFont.DoubleUnderline = true;
                        }
                        s = doubleUnderline;
                        break;
                    case StyleEnum.strike:
                        if (strike == null) {
                            strike = new Style();
                            strike.CurrentFont.Strike = true;
                        }
                        s = strike;
                        break;
                    case StyleEnum.dateFormat:
                        if (dateFormat == null) {
                            dateFormat = new Style();
                            dateFormat.CurrentNumberFormat.Number = NumberFormat.FormatNumber.format_14;
                        }
                        s = dateFormat;
                        break;
                    case StyleEnum.roundFormat:
                        if (roundFormat == null) {
                            roundFormat = new Style();
                            roundFormat.CurrentNumberFormat.Number = NumberFormat.FormatNumber.format_1;
                        }
                        s = roundFormat;
                        break;
                    case StyleEnum.borderFrame:
                        if (borderFrame == null) {
                            borderFrame = new Style();
                            borderFrame.CurrentBorder.TopStyle = Border.StyleValue.thin;
                            borderFrame.CurrentBorder.BottomStyle = Border.StyleValue.thin;
                            borderFrame.CurrentBorder.LeftStyle = Border.StyleValue.thin;
                            borderFrame.CurrentBorder.RightStyle = Border.StyleValue.thin;
                        }
                        s = borderFrame;
                        break;
                    case StyleEnum.borderFrameHeader:
                        if (borderFrameHeader == null) {
                            borderFrameHeader = new Style();
                            borderFrameHeader.CurrentBorder.TopStyle = Border.StyleValue.thin;
                            borderFrameHeader.CurrentBorder.BottomStyle = Border.StyleValue.medium;
                            borderFrameHeader.CurrentBorder.LeftStyle = Border.StyleValue.thin;
                            borderFrameHeader.CurrentBorder.RightStyle = Border.StyleValue.thin;
                            borderFrameHeader.CurrentFont.Bold = true;
                        }
                        s = borderFrameHeader;
                        break;
                    case StyleEnum.dottedFill_0_125:
                        if (dottedFill_0_125 == null) {
                            dottedFill_0_125 = new Style();
                            dottedFill_0_125.CurrentFill.PatternFill = Fill.PatternValue.gray125;
                        }
                        s = dottedFill_0_125;
                        break;
                    case StyleEnum.mergeCellStyle:
                        if (mergeCellStyle == null) {
                            mergeCellStyle = new Style();
                            mergeCellStyle.CurrentCellXf.ForceApplyAlignment = true;
                        }
                        s = mergeCellStyle;
                        break;
                    default:
                        break;
                }
                return s;
            }


        }

		private static bool equal<T> (T src, T dst) {
			if (src == null) return dst == null;
			else return src.Equals(dst);
		}
        
		public bool Equals(Style other) {
			return equal(CurrentBorder, other.CurrentBorder)
				&& equal(CurrentFill, other.CurrentFill)
				&& equal(CurrentFont, other.CurrentFont)
				&& equal(CurrentNumberFormat, other.CurrentNumberFormat)
				&& equal(CurrentCellXf, other.CurrentCellXf);
        }

        public int CompareTo(Style other) {
            return InternalID.CompareTo(other.InternalID);
        }

        public Style Copy() {
			return new Style(Name + "_copy") {
				CurrentBorder = CurrentBorder.Copy(),
				CurrentFill = CurrentFill.Copy(),
				CurrentFont = CurrentFont.Copy(),
				CurrentNumberFormat = CurrentNumberFormat.Copy(),
				CurrentCellXf = CurrentCellXf.Copy()
			};
        }

        public Style Copy(NumberFormat overwriteFormat) {
			return new Style(Name + "_copy") {
				CurrentBorder = CurrentBorder.Copy(),
				CurrentFill = CurrentFill.Copy(),
				CurrentFont = CurrentFont.Copy(),
				CurrentCellXf = CurrentCellXf.Copy(),
				CurrentNumberFormat = overwriteFormat
			};
        }

    }
}
